package com.eva.ss;

import com.eva.framework.rbac.session.SessionProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Lazy;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.ProviderManager;
import org.springframework.security.config.annotation.method.configuration.EnableMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;

import jakarta.annotation.Resource;

/**
 * Spring Security配置
 * EnableMethodSecurity注解说明
 * - securedEnabled：是否启用@Secured权限控制注解
 */
@Configuration
@EnableMethodSecurity(securedEnabled = true)
public class SecurityConfig {

    @Resource(description = "会话配置")
    private SessionProperties sessionProperties;

    @Resource(description = "认证入口，匿名用户访问时触发")
    private DefaultAuthenticationEntryPoint defaultAuthenticationEntryPoint;

    @Lazy
    @Resource(description = "预认证过滤器，用于在认证之前装载用户信息至认证上下文中，并实现交互式会话的时长控制")
    private DefaultPreAuthFilter defaultPreAuthFilter;

    @Lazy
    @Resource(description = "用户名和密码认证器")
    private UsernamePasswordAuthenticationProvider usernamePasswordAuthenticationProvider;

    @Bean
    public SecurityFilterChain filterChain(HttpSecurity httpSecurity) throws Exception {
        // 拦截配置
        httpSecurity
                .authorizeHttpRequests(auth -> auth
                    // 先指定放行路径，可在application-session.yml的exclude-path-patterns中配置
                    .requestMatchers(sessionProperties.getInterceptor().getExcludePathPatterns()).permitAll()
                    // 后指定拦截路径，可在application-session.yml的path-patterns中配置
                    .requestMatchers(sessionProperties.getInterceptor().getPathPatterns()).authenticated()
                )
                // 设置会话管理为无状态（即token机制）
                .sessionManagement(session -> session
                        .sessionCreationPolicy(SessionCreationPolicy.STATELESS))
                // 禁用CSRF（Cross-Site Request Forgery，跨站请求伪造），无状态模式下无需开启
                .csrf(AbstractHttpConfigurer::disable)
                .exceptionHandling(exceptions -> exceptions
                        // 认证入口，匿名用户访问时触发
                        .authenticationEntryPoint(defaultAuthenticationEntryPoint)
                        // 无权限或权限不足处理器，已由RbacGlobalExceptionAdvice全局捕获，此处无需配置
                        // .accessDeniedHandler()
                )
                // 添加预认证过滤器，实现认证前装载用户信息至认证上下文中
                .addFilterBefore(defaultPreAuthFilter, UsernamePasswordAuthenticationFilter.class)
        ;
        return httpSecurity.build();
    }

    /**
     * 注册认证管理器，并提供认证器实现实例
     *
     * @return AuthenticationManager
     */
    @Bean
    public AuthenticationManager authenticationManager() {
        // 可注册多个认证器
        // return new ProviderManager(AProvider, BProvider, ...);

        // 默认仅实现用户名和密码认证器
        return new ProviderManager(usernamePasswordAuthenticationProvider);
    }
}
